<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MSPM0G3507 PWM驱动编码电机完整教程</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
        }
        h1 {
            color: #0066cc;
            border-bottom: 2px solid #0066cc;
            padding-bottom: 10px;
        }
        h2 {
            color: #0066cc;
            margin-top: 30px;
        }
        h3 {
            color: #333;
            margin-top: 25px;
        }
        pre {
            background-color: #f5f5f5;
            padding: 15px;
            border-radius: 5px;
            overflow-x: auto;
        }
        code {
            font-family: Consolas, Monaco, 'Andale Mono', monospace;
        }
        .note {
            background-color: #e6f7ff;
            border-left: 4px solid #1890ff;
            padding: 10px 15px;
            margin: 15px 0;
        }
        .warning {
            background-color: #fff7e6;
            border-left: 4px solid #fa8c16;
            padding: 10px 15px;
            margin: 15px 0;
        }
        img {
            max-width: 100%;
            height: auto;
            display: block;
            margin: 20px auto;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin: 20px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px 12px;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
        }
        .step {
            background-color: #f9f9f9;
            padding: 15px;
            margin: 20px 0;
            border-radius: 5px;
            counter-increment: step-counter;
        }
        .step::before {
            content: "步骤 " counter(step-counter);
            font-weight: bold;
            display: block;
            margin-bottom: 10px;
            color: #0066cc;
        }
    </style>
</head>
<body>
    <h1>MSPM0G3507 PWM驱动编码电机完整教程</h1>
    
    <h2>1. 基础知识</h2>
    
    <h3>1.1 PWM基础</h3>
    <p>脉冲宽度调制(PWM)是一种通过调节高低电平的占空比来控制平均电压的技术。</p>
    <ul>
        <li><strong>周期(Period)</strong>：一个完整PWM波形的时间长度</li>
        <li><strong>占空比(Duty Cycle)</strong>：高电平时间占整个周期的百分比</li>
        <li><strong>频率(Frequency)</strong>：PWM信号每秒重复的次数</li>
    </ul>
    <div class="note">
        <p>对于电机控制，PWM频率通常设置在1kHz~20kHz范围内。占空比越高，电机转速越快。</p>
    </div>
    
    <h3>1.2 编码电机基础</h3>
    <p>编码电机是指带有位置反馈装置（编码器）的电机，可以监测电机的转速、方向和位置。</p>
    <ul>
        <li><strong>增量式编码器</strong>：输出A、B两相正交信号，通过相位差判断方向</li>
        <li><strong>绝对式编码器</strong>：直接输出绝对位置信息</li>
    </ul>
    <p>本教程主要讨论增量式编码器的应用，它通过A、B两相信号的相位差来判断旋转方向。</p>
    
    <h2>2. 硬件准备</h2>
    
    <h3>2.1 MSPM0G3507芯片介绍</h3>
    <p>MSPM0G3507是TI公司的低功耗微控制器，具有以下特点：</p>
    <ul>
        <li>32MHz ARM Cortex-M0+内核</li>
        <li>多达4个高级定时器，支持多通道PWM输出</li>
        <li>丰富的GPIO资源</li>
        <li>低功耗模式</li>
    </ul>
    
    <h3>2.2 电路连接</h3>
    <p>电机驱动需要以下连接：</p>
    <table>
        <tr>
            <th>功能</th>
            <th>MSPM0G3507引脚</th>
            <th>外部连接</th>
        </tr>
        <tr>
            <td>PWM输出</td>
            <td>PA0 (TIMA0_CCP0)</td>
            <td>电机驱动芯片IN1</td>
        </tr>
        <tr>
            <td>PWM输出</td>
            <td>PA1 (TIMA0_CCP1)</td>
            <td>电机驱动芯片IN2</td>
        </tr>
        <tr>
            <td>编码器A相</td>
            <td>PA5</td>
            <td>编码器A相输出</td>
        </tr>
        <tr>
            <td>编码器B相</td>
            <td>PA4</td>
            <td>编码器B相输出</td>
        </tr>
    </table>
    
    <div class="note">
        <p>对于直流电机，通常需要H桥驱动芯片（如L298N、DRV8833等）连接在MCU和电机之间。</p>
    </div>
    
    <h2>3. 开发环境搭建</h2>
    
    <h3>3.1 Code Composer Studio(CCS)安装</h3>
    <div class="step">
        <p>访问TI官网下载最新版CCS：<a href="https://www.ti.com/tool/CCSTUDIO" target="_blank">https://www.ti.com/tool/CCSTUDIO</a></p>
        <p>按照安装向导进行安装，确保选择MSPM0芯片支持。</p>
    </div>
    
    <h3>3.2 驱动库安装</h3>
    <div class="step">
        <p>安装MSPM0 SDK：在CCS中，通过"Resource Explorer"或"App Center"安装。</p>
        <p>安装路径：View > Resource Explorer > TI Resource Explorer > Software > MSPM0 SDK</p>
    </div>
    
    <h2>4. 项目创建与配置</h2>
    
    <h3>4.1 创建新项目</h3>
    <div class="step">
        <p>打开CCS，选择File > New > CCS Project</p>
        <p>选择目标设备：MSPM0G3507</p>
        <p>选择项目模板：Empty Project with SysConfig</p>
        <p>填写项目名称，如"PWM_Motor_Control"</p>
    </div>
    
    <h3>4.2 使用SysConfig进行图形化配置</h3>
    <div class="step">
        <p>双击项目中的.syscfg文件打开SysConfig配置工具</p>
        <p>配置PWM输出：</p>
        <ol>
            <li>在左侧面板选择"PWM"，点击"Add"</li>
            <li>设置PWM名称为"PWM_motor"</li>
            <li>配置PWM参数：
                <ul>
                    <li>时钟源：BUSCLK</li>
                    <li>分频比：4</li>
                    <li>PWM模式：Edge-aligned</li>
                    <li>PWM周期：1000 (根据需求调整)</li>
                </ul>
            </li>
            <li>配置PWM通道：
                <ul>
                    <li>设置CCP0和CCP1通道为PWM输出</li>
                    <li>分配CCP0到PA0，CCP1到PA1</li>
                    <li>设置初始占空比：500 (50%)</li>
                </ul>
            </li>
        </ol>
        <p>配置GPIO输入引脚（用于编码器）：</p>
        <ol>
            <li>在左侧面板选择"GPIO"，点击"Add"</li>
            <li>添加编码器引脚：
                <ul>
                    <li>设置PA5为输入模式，启用上升沿中断</li>
                    <li>设置PA4为输入模式</li>
                </ul>
            </li>
            <li>配置中断优先级和触发条件</li>
        </ol>
        <p>点击"Generate"生成配置代码</p>
    </div>
    
    <h2>5. 代码实现</h2>
    
    <h3>5.1 编码器相关代码</h3>
    <p>创建encoder.h文件：</p>
    <pre><code>/*
 * encoder.h
 * 编码器驱动头文件
 */

#ifndef ENCODER_H_
#define ENCODER_H_

#include "ti_msp_dl_config.h"
#include &lt;stdint.h&gt;
#include &lt;stdbool.h&gt;

// 编码器结构体定义
typedef struct {
    volatile int32_t count;    // 计数值
    volatile int8_t direction; // 方向: 1=正向, -1=反向, 0=静止
    uint8_t last_state;        // 上一个编码器状态
} Encoder_t;

// 函数声明
void Encoder_Init(void);
int32_t Encoder_GetCount(void);
int8_t Encoder_GetDirection(void);
void Encoder_Reset(void);
void Encoder_Update(void);

#endif /* ENCODER_H_ */</code></pre>

    <p>创建encoder.c文件：</p>
    <pre><code>/*
 * encoder.c
 * 编码器驱动实现
 */

#include "encoder.h"

// 编码器实例
static Encoder_t encoder = {0, 0, 0};

// 编码器状态转换表
// 00->01->11->10->00 (正向)
// 00->10->11->01->00 (反向)
static const int8_t encoder_states[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};

// 编码器初始化
void Encoder_Init(void) 
{
    // 读取初始状态
    uint8_t enc_a = (DL_GPIO_getPins(GPIOA) & DL_GPIO_PIN_5) ? 1 : 0;
    uint8_t enc_b = (DL_GPIO_getPins(GPIOA) & DL_GPIO_PIN_4) ? 1 : 0;
    encoder.last_state = (enc_a << 1) | enc_b;
}

// 更新编码器状态
void Encoder_Update(void)
{
    // 读取当前状态
    uint8_t enc_a = (DL_GPIO_getPins(GPIOA) & DL_GPIO_PIN_5) ? 1 : 0;
    uint8_t enc_b = (DL_GPIO_getPins(GPIOA) & DL_GPIO_PIN_4) ? 1 : 0;
    uint8_t new_state = (enc_a << 1) | enc_b;
    
    // 如果状态有变化
    if (new_state != encoder.last_state) {
        // 计算方向
        uint8_t combined_state = (encoder.last_state << 2) | new_state;
        int8_t direction = encoder_states[combined_state & 0x0F];
        
        // 更新计数器和方向
        encoder.count += direction;
        if (direction != 0) {
            encoder.direction = direction;
        }
        
        // 保存当前状态
        encoder.last_state = new_state;
    }
}

// 获取编码器计数
int32_t Encoder_GetCount(void) 
{
    return encoder.count;
}

// 获取编码器方向
int8_t Encoder_GetDirection(void) 
{
    return encoder.direction;
}

// 重置编码器计数
void Encoder_Reset(void) 
{
    encoder.count = 0;
}

// GROUP0中断处理函数(用于PA5的中断)
void GROUP0_IRQHandler(void)
{
    // 检查中断标志并更新编码器状态
    Encoder_Update();
    
    // 清除中断标志
    // 注意：根据实际的TI库函数进行调整
    DL_GPIO_clearInterruptStatus(GPIOA, DL_GPIO_PIN_5);
}</code></pre>

    <h3>5.2 PWM电机控制代码</h3>
    <p>创建motor.h文件：</p>
    <pre><code>/*
 * motor.h
 * 电机控制头文件
 */

#ifndef MOTOR_H_
#define MOTOR_H_

#include "ti_msp_dl_config.h"
#include &lt;stdint.h&gt;

// 电机方向定义
typedef enum {
    MOTOR_FORWARD = 0,
    MOTOR_BACKWARD,
    MOTOR_STOP
} MotorDirection_t;

// 函数声明
void Motor_Init(void);
void Motor_SetSpeed(uint16_t speed);
void Motor_SetDirection(MotorDirection_t direction);
void Motor_Stop(void);

#endif /* MOTOR_H_ */</code></pre>

    <p>创建motor.c文件：</p>
    <pre><code>/*
 * motor.c
 * 电机控制实现
 */

#include "motor.h"

// 电机当前方向
static MotorDirection_t current_direction = MOTOR_STOP;

// 初始化电机控制
void Motor_Init(void)
{
    // PWM配置已在SysConfig中完成
    // 默认设置为停止状态
    Motor_Stop();
}

// 设置电机速度
void Motor_SetSpeed(uint16_t speed)
{
    // 限制速度范围(0-1000)
    if (speed > 1000) {
        speed = 1000;
    }
    
    // 根据当前方向设置PWM
    if (current_direction == MOTOR_FORWARD) {
        DL_Timer_setCaptureCompareValue(PWM_motor_INST, speed, DL_TIMER_CC_0_INDEX);
        DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_1_INDEX);
    } 
    else if (current_direction == MOTOR_BACKWARD) {
        DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_0_INDEX);
        DL_Timer_setCaptureCompareValue(PWM_motor_INST, speed, DL_TIMER_CC_1_INDEX);
    }
}

// 设置电机方向
void Motor_SetDirection(MotorDirection_t direction)
{
    current_direction = direction;
    
    switch(direction) {
        case MOTOR_FORWARD:
            // 正转：IN1=PWM, IN2=0
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 500, DL_TIMER_CC_0_INDEX);
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_1_INDEX);
            break;
            
        case MOTOR_BACKWARD:
            // 反转：IN1=0, IN2=PWM
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_0_INDEX);
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 500, DL_TIMER_CC_1_INDEX);
            break;
            
        case MOTOR_STOP:
        default:
            // 停止：IN1=0, IN2=0
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_0_INDEX);
            DL_Timer_setCaptureCompareValue(PWM_motor_INST, 0, DL_TIMER_CC_1_INDEX);
            break;
    }
}

// 停止电机
void Motor_Stop(void)
{
    Motor_SetDirection(MOTOR_STOP);
}</code></pre>

    <h3>5.3 主程序实现</h3>
    <p>修改main.c文件：</p>
    <pre><code>/*
 * main.c
 * PWM驱动编码电机主程序
 */

#include "ti_msp_dl_config.h"
#include "encoder.h"
#include "motor.h"

// 延时函数
void delay_ms(uint32_t ms)
{
    uint32_t i;
    for (i = 0; i < ms * 1000; i++) {
        __NOP();  // 空操作指令
    }
}

// PID控制结构体
typedef struct {
    float Kp;         // 比例系数
    float Ki;         // 积分系数
    float Kd;         // 微分系数
    float target;     // 目标值
    float error;      // 当前误差
    float lastError;  // 上次误差
    float integral;   // 积分项
} PID_t;

// PID计算函数
float PID_Calculate(PID_t *pid, float current)
{
    // 计算误差
    pid->error = pid->target - current;
    
    // 计算PID各项
    float proportional = pid->Kp * pid->error;
    pid->integral += pid->error;
    float integral = pid->Ki * pid->integral;
    float derivative = pid->Kd * (pid->error - pid->lastError);
    
    // 保存当前误差
    pid->lastError = pid->error;
    
    // 返回PID计算结果
    return proportional + integral + derivative;
}

// 主函数
int main(void)
{
    // 初始化系统配置
    SYSCFG_DL_init();
    
    // 初始化编码器
    Encoder_Init();
    
    // 初始化电机
    Motor_Init();
    
    // 初始化PID结构体
    PID_t speedPID = {
        .Kp = 0.5f,
        .Ki = 0.1f,
        .Kd = 0.01f,
        .target = 100.0f,  // 目标速度
        .error = 0.0f,
        .lastError = 0.0f,
        .integral = 0.0f
    };
    
    // 设置电机方向为正转
    Motor_SetDirection(MOTOR_FORWARD);
    
    // 初始速度
    uint16_t motorSpeed = 500;
    Motor_SetSpeed(motorSpeed);
    
    // 上次编码器读数和时间
    int32_t lastCount = 0;
    uint32_t lastTime = 0;
    float currentSpeed = 0;
    
    // 启用中断
    NVIC_EnableIRQ(GROUP0_INT_IRQn);
    
    // 主循环
    while(1) {
        // 每100ms计算一次速度和PID
        delay_ms(100);
        
        // 读取编码器计数
        int32_t currentCount = Encoder_GetCount();
        
        // 计算速度(计数差/时间)
        currentSpeed = (float)(currentCount - lastCount) * 10.0f;  // *10 转换为每秒计数
        lastCount = currentCount;
        
        // 计算PID控制输出
        float pidOutput = PID_Calculate(&speedPID, currentSpeed);
        
        // 调整电机速度
        motorSpeed += (int16_t)pidOutput;
        
        // 限制速度范围
        if(motorSpeed > 1000) motorSpeed = 1000;
        if(motorSpeed < 0) motorSpeed = 0;
        
        // 更新电机速度
        Motor_SetSpeed(motorSpeed);
    }
}</code></pre>
    
    <h2>6. 编译和下载</h2>
    
    <h3>6.1 项目编译</h3>
    <div class="step">
        <p>在CCS中点击项目名称，选择"Build Project"</p>
        <p>确保编译无错误</p>
    </div>
    
    <h3>6.2 程序下载到目标板</h3>
    <div class="step">
        <p>连接开发板到电脑</p>
        <p>在CCS中点击"Debug"按钮</p>
        <p>程序下载完成后，点击"Resume"开始运行程序</p>
    </div>
    
    <h2>7. 电机控制高级功能</h2>
    
    <h3>7.1 闭环速度控制</h3>
    <p>主程序已实现基本PID闭环速度控制。调整PID参数可以获得更好的控制效果：</p>
    <ul>
        <li><strong>Kp</strong>：比例增益，影响系统响应速度</li>
        <li><strong>Ki</strong>：积分增益，消除稳态误差</li>
        <li><strong>Kd</strong>：微分增益，抑制超调和振荡</li>
    </ul>
    
    <h3>7.2 位置控制</h3>
    <p>位置控制可以通过修改main.c中的PID控制逻辑实现：</p>
    <pre><code>// 位置控制PID
PID_t positionPID = {
    .Kp = 2.0f,
    .Ki = 0.0f,
    .Kd = 0.5f,
    .target = 1000.0f,  // 目标位置(编码器计数)
    .error = 0.0f,
    .lastError = 0.0f,
    .integral = 0.0f
};

// 在主循环中
float positionOutput = PID_Calculate(&positionPID, (float)Encoder_GetCount());

// 根据位置PID输出设置电机方向和速度
if (positionOutput > 0) {
    Motor_SetDirection(MOTOR_FORWARD);
    Motor_SetSpeed((uint16_t)(positionOutput > 1000 ? 1000 : positionOutput));
} else if (positionOutput < 0) {
    Motor_SetDirection(MOTOR_BACKWARD);
    Motor_SetSpeed((uint16_t)(positionOutput < -1000 ? 1000 : -positionOutput));
} else {
    Motor_Stop();
}</code></pre>
    
    <h2>8. 常见问题与解决方案</h2>
    
    <h3>8.1 编译错误</h3>
    <div class="warning">
        <p><strong>问题</strong>：未定义的符号（如PWM_motor_INST）</p>
        <p><strong>解决方案</strong>：检查SysConfig配置中的名称是否与代码中使用的一致。重新生成SysConfig配置文件。</p>
    </div>
    
    <h3>8.2 电机不转动</h3>
    <div class="warning">
        <p><strong>问题</strong>：配置正确但电机不转动</p>
        <p><strong>解决方案</strong>：
        <ul>
            <li>检查电机驱动电路连接是否正确</li>
            <li>确认PWM输出是否正常，可以用示波器测量</li>
            <li>检查电机和驱动芯片供电是否充足</li>
        </ul>
        </p>
    </div>
    
    <h3>8.3 编码器计数不准确</h3>
    <div class="warning">
        <p><strong>问题</strong>：编码器计数不稳定或不准确</p>
        <p><strong>解决方案</strong>：
        <ul>
            <li>添加上拉电阻到编码器信号线</li>
            <li>检查编码器接线是否正确</li>
            <li>添加硬件或软件滤波</li>
            <li>调整中断优先级</li>
        </ul>
        </p>
    </div>
    
    <h2>9. 进阶学习</h2>
    
    <p>完成本教程后，您可以考虑探索以下进阶主题：</p>
    <ul>
        <li>多电机控制</li>
        <li>电机电流监测与限流保护</li>
        <li>通讯接口控制（UART、CAN等）</li>
        <li>使用FreeRTOS实现多任务控制</li>
        <li>电机参数自动辨识</li>
    </ul>
    
    <h2>10. 参考资料</h2>
    
    <ul>
        <li><a href="https://www.ti.com/product/MSPM0G3507" target="_blank">MSPM0G3507产品页面</a></li>
        <li><a href="https://www.ti.com/lit/ds/symlink/mspm0g3507.pdf" target="_blank">MSPM0G3507数据手册</a></li>
        <li><a href="https://www.ti.com/lit/ug/slau816a/slau816a.pdf" target="_blank">MSPM0 DriverLib用户指南</a></li>
        <li><a href="https://www.ti.com/tool/MSPM0SDK" target="_blank">MSPM0 SDK</a></li>
    </ul>
    
    <div class="note">
        <p>本教程提供的代码仅供学习参考，实际应用中请根据具体需求进行调整和优化。</p>
    </div>
</body>
</html>